public class ThreadWait2 {
    public static void main(String[] args) throws InterruptedException {
        // wait 和 notify 操作

        Object object = new Object();

        Thread thread1 = new Thread(()->{
            // 这个线程负责等待
            System.out.println("thread1: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("thread1: wait 之后");
        });

        Thread thread2 = new Thread(()->{
           // 通知线程
            System.out.println("thread2: notify 之前");
            synchronized (object) {

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                // notify 务必要获取到锁 才能进行通知
                object.notify(); // 这两个线程针对的对象务必是同一个对象1

                // ctrl alt + t
            }

            System.out.println("thread2: notify 之后");
        });

        // 注意： synchronized (object) object.wait();  synchronized (object) object.notify(); 这四个对象必须都相同，才能够生效
        // 此处的通知 必须和 wait 配对，如果wait 使用的对象和notify使用的对象 不同的话 此时notify不会有任何效果，（notify 只能唤醒在同一个对象上等待的线程）

        thread1.start();
        // 此处 sleep 500 是大概率 让当前的 thread1 先执行 wait
        // 只有极端情况，当电脑特别卡的情况，电脑线程调度的时间已经超过了500ms
        Thread.sleep(500);
        thread2.start();
        // 如果我们代码 写作 thread1.start();  thread2.start();  由于线程调度的不确定性，此时不能保证一定是先执行wait 还是先执行notify
        // 如果是调用notify，此时没有wait ，此处的wait 是无法被唤醒的
        // 那么就是无效通知 ， 但是也不会有什么副作用
        // 但是还是要保证先执行wait 再 执行 notify
        // 所以要加一个休眠时间

        // 这样运行之后 很明显看到 先是显示 wait 之前 很明显wait 操作阻塞了。没有看到 wait 之后的打印
        // 接下来执行到了 thread2 ，thread2 进行了 notify之后才会把thread1唤醒
        // 只要 thread2 不进行notify 操作 thread1 就会始终 wait 下去

        // 所以 wait 无参数版本，就是死等
        // wait 带参数的版本就是，指定了最大等待时间
        // 带参数版本就是和sleep 有点像，但还是有本质的上的差别
        // 虽然都能被提前唤醒
        // notify 唤醒 wait，这里是不会有任何异常的（正常的业务逻辑）
        // interrupt 唤醒 sleep 则是出了异常（表示一个 出问题了的逻辑）

        // notify 唤醒的线程是随机唤醒的一个等待线程
        // 可以用多组不同的对象来解决这种情况

    }
}
